#include <mips/regdef.h>
#include <sys/syscall.h>

.data

invalid_char:	.asciz	"\nINPUT_ERROR: Binary input file should only contain 1's and 0's.\n"
nobytescount:	.asciz	"\nINPUT_ERROR: Binary input file has not an entire amount of bytes.\n"

char_in: .space 2		# espacio de memoria para alojar el byte de lectura mas el byte null
char_out: .space 2		# caracter a imprimir luego del ingreso de 8 bits
ASCII_0: .ascii	"0"		# caracter 0 para ser comparado con la entrada del usuario
ASCII_1: .ascii	"1" 		# caracter 1 para ser comparado con la entrada del usuario
ASCII_SPACE: .ascii " "		# caracter espacio para ser comparado con la entrada del usuario
ASCII_ENTER: .ascii "\n"	# caracter enter para ser comparado con la entrada del usuario

	.text
	.align	2
	.globl	main
	.ent	main
# valores definidos para el OFFSET de las variables guardadas en el stack, 
# mas el largo de los mensages de error y los codigos de retorno.

#define	MAIN_SF_SIZE	48
#define	MAIN_RA		40
#define	MAIN_FP		36
#define	MAIN_GP		32
#define	RETURN_VALUE	16
#define INPUT_CHAR	20
#define	PTR_OUTCHAR	24
#define	ITERATOR	28

#define countmsg_length	65
#define invalid_length	64


#define SYSERROR	2
#define	SUCCEEDED	0
#define	FAILURE		1

main:
	.frame	$fp, MAIN_SF_SIZE, ra		
	.set	noreorder
	.cpload	t9
	.set	reorder
	subu	sp, sp, MAIN_SF_SIZE

	sw	ra, MAIN_RA(sp)		# guardo los valores de registro de stack
	sw	$fp, MAIN_FP(sp)	# 32=gp; 36=fp; 40=ra; 44=align	
	sw	gp, MAIN_GP(sp)		# 16=return_value; 20=input_char; 24=ptr_outchar; 28=i; 
	move	$fp, sp
	la	t0, char_out
	sw	t0, PTR_OUTCHAR($fp)	# guardo puntero de char_out en stack
	sw	zero, ITERATOR($fp)		# i=0: itero sobre los 8 bits de entrada para cada char
	
while_char:
	li	a0, 0			# leo los bits desde stdin
	la	a1, char_in		# guardo el char leido en la direccion de char_in
	li	a2, 1
	li	v0, SYS_read		
	syscall
	bne	a3, zero, syserror	# hubo un error al leer, termino el programa
	beq	v0, zero, while_out	# lei EOF, termino el programa
	lbu	t0, char_in			# guardo el byte char en el stack
	sb	t0, INPUT_CHAR($fp)

# evaluo que la entrada sean caracteres validos:
# " " o "\n" no se procesan y continuo; '0' y '1' validos

ifs:
	lb	t1, INPUT_CHAR($fp)	
	
	lbu	t0, ASCII_0		# 0x30 '0' 
	beq	t1, t0, input_ok
	lbu	t0, ASCII_1		# 0x31 '1'
	beq	t1, t0, input_ok
	lbu	t0, ASCII_SPACE		# 0x20 ' '
	beq	t1, t0, while_char
	lbu	t0, ASCII_ENTER		# 0xa '/n'
	beq	t1, t0, while_char
	
print_invalid:
	li	a0, 2				# escribo en stderr mensage de error
	la	a1, invalid_char	# el caracter ingresado es invalido
	li	a2, invalid_length
	li	v0, SYS_write
	syscall
	bne	a3, zero, syserror
	li	t0, FAILURE			# guardo 1 como codigo de retorno de fallo
	sw	t0, RETURN_VALUE($fp)
	b	end

input_ok:
	lw	t0, ITERATOR($fp)		# incremento i en 1. itera sobre los 8 bits del byte
	addu	t0, t0, 1
	sw	t0, ITERATOR($fp)
	lw	t0, INPUT_CHAR($fp)
	addu	t0, t0, -48			# inputc - '0': convierto '0' o '1' en 0 y 1 enteros
	sw	t0, INPUT_CHAR($fp)
	li	t1, 8			
	lw	t0, ITERATOR($fp)
	subu	t1, t1, t0			# 8 - i
	lw	t0, INPUT_CHAR($fp)		# pondero el bit a la posicion correspondiente para sumarlo al byte final
	sll	t1, t0, t1				# output_char += input_char << (8 - i)  
	lw	t2, PTR_OUTCHAR($fp)	# guardo la suma parcial en el byte de salida..
	lbu	t3, 0(t2)				
	addu	t3, t3, t1
	sb	t3, 0(t2)		
	lw	t1, ITERATOR($fp)
	li	t0, 8					# if i == 8: lei un byte entero, entonces lo imprimo.
	bne	t1, t0, while_char
	sw	zero, ITERATOR($fp)		# reinicio i
	
	li	a0, 1					# escribo en stdout
	lw	a1, PTR_OUTCHAR($fp)	# escribo el caracter formado por las 8 entradas de 0s y 1s
	li	a2, 1
	li	v0, SYS_write
	syscall
	bne	a3, zero, syserror		# evaluo errores en la escritura
	lw	t1, PTR_OUTCHAR($fp)
	sb	zero, 0(t1)				# reinicio el byte de char a imprimir en el puntero
	b	while_char

while_out:
	lw	t0, ITERATOR($fp)
	beq	t0, zero, success		# if i!=0, se llamo a EOF habiendo ingresado un numero
	li	a0, 2					# no multiplo de 8 bits.
	la	a1, nobytescount		# imprimo error de cantidad de bits invalida
	li	a2, countmsg_length
	li	v0, SYS_write
	syscall
	bne	a3, zero, syserror		# evaluo por error en la escritura
	li	t0, FAILURE
	sw	t0, RETURN_VALUE($fp)	# guardo valor de retorno con codigo de error 1
	b	end

syserror:
	li	t0, SYSERROR			# error al usar syscall, termino el programa con codigo de retorno 2
	sw	t0, RETURN_VALUE($fp)
	b end	

success:
	li	t0, SUCCEEDED			# el programa termino exitoso.
	sw	t0, RETURN_VALUE($fp)	# guardo codigo de retorno 0.
end:
	lw	v0, RETURN_VALUE($fp)	# cargo codigo de retorno en v0
	move	sp, $fp				# restauro los valores de los registros 
	lw	ra, MAIN_RA(sp)
	lw	$fp, MAIN_FP(sp)
	lw	gp, MAIN_GP(sp)
	addu	sp, sp, MAIN_SF_SIZE	# destruyo stack frame del programa
	j	ra
	.end	main
